#ifndef GKO_POOL_INCLUDE_ASYNC_POOL_H
#define GKO_POOL_INCLUDE_ASYNC_POOL_H
/// ==================== Headers
#include <cstdlib>
#include <cstdio>
#include <cstring>
#if __linux
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/resource.h>
#include <pthread.h>
#include <errno.h>
#endif // linux deps headers

#include <vector>

#include "ares.h"
#include "event.h"
#include "gp_memory.h"
#include "dict.h"

#include "gp_error.h" // TODO: rename headers 
/// ====================
static const int RBUF_SZ = SLOT_SIZE;
static const int WBUF_SZ = SLOT_SIZE;
static const u_int8_t F_KEEPALIVE = 0x01;
static const u_int8_t F_CONN_REUSE = 0x02;

/// @desc: Thread Worker
struct thread_worker
{
    int id;
    pthread_t tid;
    struct event_base* ev_base;
    struct event ev_notify;
    struct event ev_cleantimeout;

    int notify_recv_fd;
    int notify_send_fd;
    std::set<int> conn_set;

    ares_channel dns_channel;

    gkoAlloc mem;

    void* userData;

    /// @desc: put conn into current thread `conn_set`
    void add_conn(int c_id);
    /// @desc: delete conn from current thread `conn_set`
    void del_conn(int c_id);
};

enum conn_state 
{
    conn_nouse = 0,
    conn_listening,
    conn_waiting,
    conn_read,
    conn_nread,

    conn_parse_header,
    conn_parse_cmd,
    conn_write,
    conn_mwrite,
    conn_state_renew,
    conn_closing,

    /// @sepline: for client side
    conn_connecting = 101,
    conn_dns_cache,
    conn_resolving,
    conn_max_state // TODO: add docs for this enum
};

enum conn_type
{
    coming_conn = 0, // default,
    active_conn = 1
};

/// @desc: Connection client
struct conn_client
{
    int id;
    int worker_id;
    int client_fd;
    long task_id;
    long sub_task_id;

    in_addr_t client_addr;
    int client_port; // why not unsigned
    time_t conn_time;

    func_t handle_client;
    struct event event;
    std::vector<struct event*> ev_dns_vec;

    enum conn_state state;
    enum error_no err_no;
    enum conn_type type;

    int ev_flags;

    void* userData;

    int r_buf_arena_id;
    char* read_buffer;
    unsigned int rbuf_size;
    unsigned int need_read;
    unsigned int have_read;

    int w_buf_arena_id;
    char* __write_buffer;
    char* write_buffer;

    unsigned int wbuf_size;
    unsigned int __need_write;
    unsigned int need_write;
    unsigned int have_write;

    char client_hostname[MAX_HOST_NAME + 1];
};

struct conn_server
{
    char is_server;
    int listen_fd;
    struct sockaddr_in listen_addr;
    in_addr_t srv_addr;

    int srv_port;
    unsigned int start_time;
    int nonblock;
    int listen_queue_length;
    int tcp_send_buffer_size;
    int tcp_recv_buffer_size;

    int send_timeout;
    int tcp_reuse;
    int tcp_nodelay;

    struct event ev_accept;

    ProcessHandler_t on_data_callback;
};

enum aread_result
{
    READ_DATA_RECEIVED,
    READ_DATA_RECEIVED,
    READ_NEED_MORE,
    READ_NO_DATA_RECEIVED,

    READ_ERROR,
    READ_MEMORY_ERROR
};

enum awrite_result
{
    WRITE_DATA_SENT,
    WRITE_HEADER_SENT,
    WRITE_SENT_MORE,
    WRITE_NO_DATA_SENT,

    WRITE_ERROR
};

int defaultHMTRHandler(void* p, const char* buf, const int len);

struct gko_pool
{
    /// @desc: public static field
    static s_host_t gko_serv;
    static gko_pool *getInstance();
    /// @desc: public funcs
    void setProcessHandler(ProcessHandler_t func_list);
    void setReportHandler(ProcessHandler_t report_func);
    void setHMTRHandler(HMTRHandler HMTR_func);
    int getPort() const;
    void setPort(int port);
    s_option_t* getOption() const;
    void setOption(s_option_t* option);

    thread_worker* getWorker(const struct conn_client* _1);
    /// @desc: global run func
    int gko_run();
    int gko_loopexit(int timeout);

    int make_active_connect(const char* host, const int port, int len,
                            const char* cmd, const long task_id = -1,
                            const long sub_task_id = -1, const u_int8_t flag = 0,
                            const int wrote = 0);
/// @desc: private 
private:
    static gko_pool* _instance;
    /// @desc: global lock
    static pthread_mutex_t instance_lock;
    static pthread_mutex_t conn_list_lock;
    static pthread_mutex_t thread_list_lock;
    static pthread_mutex_t DNS_cache_lock;

    int g_curr_thread;
    int g_curr_conn;

    thread_worker** g_worker_list;
    struct event_base* g_ev_base;
    int g_total_clients;
    int g_total_connect;
    
    struct conn_client** g_client_list;
    struct conn_server* g_server;

    int port;
    s_option_t* option;
    ProcessHandler_t pHandler;
    ReportHandler_t reportHandler;
    HMTRHandler_t HMTRHandler;
    dict* DNSDict;

    static void conn_send_data(void* c);
    static void conn_tcp_server_accept(int fd, short ev, void* arg);
    static void* thread_worker_init(void* arg);
    static void thread_worker_process(int fd, short ev, void* arg);
    static void conn_buffer_init(conn_client* client);
    static bool update_event(conn_client* c, const int new_flags);
    static void worker_event_handler(const int fd, const short which, void* arg);
    static void clean_handler(const int fd, const short which, void* arg);
    static enum aread_result aread(conn_client* c);
    static enum awrite_result awrite(conn_client* c);
    static void state_machine(conn_client* c);
    
    static int del_dns_event(conn_client* c);
    static void dns_callback(void* arg, int status, int timeouts,
                             struct hostent* host);
    static void dns_ev_callback(int fd, short ev, void* arg);

    /// @desc: public non static
    int nb_connect(struct conn_client* conn);
    int connect_hosts(const std::vector<s_host_t>& host_vec,
                      std::vector<struct conn_client>* conn_vec);
    int disconnect_hosts(std::vector<struct conn_client>& conn_vec);

    dict* init_dns_cache(void);
    int try_dns_cache(conn_client* c);
    void update_dns_cache(conn_client* c, in_addr_t addr);
    
    int clean_conn_timeout(thread_worker* worker, time_t now);
    int thread_worker_new(int id);
    int thread_list_find_next(void);
    int conn_client_list_init(void);
    int gko_async_server_base_init(void);

    int conn_tcp_server(struct conn_server* c_s);
    struct conn_client* add_new_conn_client(int client_fd);

    int conn_client_list_find_free();
    int conn_renew(struct conn_client* client);
    int conn_client_clear(struct conn_client* client);
    int conn_client_free(struct conn_client* client);
    struct conn_client* conn_client_list_get(int id);
    void thread_worker_dispatch(int c_id);
    void thread_worker_dispatch(int c_id, int worker_id);
    int thread_init();

    /// @desc: private init func
    gko_pool(const int pt);
    gko_pool();
};

#endif // header file protect
// END: CODEREVIEW: